﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

using Lilium.Web.Mvc.Html;
using Lilium.Web.Mvc.State;
using System.Linq.Expressions;

namespace Lilium.Web.Mvc.EntityFilters
{
    public interface IPager : IEntityFilter, IStatefulObject
    {
        string GetStateParameterName(Expression<Func<PagerState, object>> getStateParamFunc);

        int EntityCount { get; }

        int PageIndex { get; }

        int PageCount { get; }

        int PageSize { get; set; }
    }

    public abstract class Pager
    {
        public static IPager GetPager(ControllerBase controller, Type entityType)
        {
            if (controller == null) throw new ArgumentNullException("controller");

            return (IPager)controller.GetService(GetPagerType(entityType), true);
        }


        public static Type GetPagerType(Type entityType)
        {
            if (entityType == null) throw new ArgumentNullException("entityType");

            return typeof(Pager<>).MakeGenericType(entityType);
        }
    }

    public sealed class Pager<TEntity> : EntityFilter<TEntity, PagerState>, IPager
    {
        public Pager(ControllerBase controller)
            : base(controller)
        {
        }

        public override IQueryable<TEntity> Apply(IQueryable<TEntity> entities)
        {
            if (entities == null) throw new ArgumentNullException("entities");

            EntityCount = entities.Count();

            if (State.PageSize != 0)
            {
                if (StartIndex >= EntityCount)
                    State.PageIndex = PageCount - 1;

                if (StartIndex != 0)
                    entities = entities.Skip(StartIndex);

                if ((State.PageSize != 0) && (State.PageSize < EntityCount))
                    entities = entities.Take(State.PageSize);
            }
            return entities;
        }

        #region public int EntityCount

        private int m_EntityCount;

        public int EntityCount
        {
            get { return m_EntityCount; }
            private set { m_EntityCount = value; }
        }

        #endregion

        #region public int PageCount

        public int PageCount
        {
            get
            {
                if ((PageSize != 0) && (PageSize < EntityCount))
                {
                    int rem;
                    var res = Math.DivRem(EntityCount, PageSize, out rem);
                    if (rem != 0)
                        res++;
                    return res;
                }
                else
                    return 1;
            }
        }

        #endregion

        #region public int PageIndex

        public int PageIndex
        {
            get
            {
                return State.PageIndex;
            }
            set
            {
                State.PageIndex = value;
            }
        }

        #endregion

        #region public int PageSize

        public int PageSize
        {
            get { return State.PageSize; }
            set { State.PageSize = value; }
        }

        #endregion

        #region public int StartIndex

        public int StartIndex
        {
            get
            {
                return State.PageIndex * State.PageSize;
            }
        }

        #endregion

        protected override PagerState OnGetState(string actionName)
        {
            if (!string.IsNullOrWhiteSpace(actionName))
            {
                var pagerAction = (PagerAction)Enum.Parse(typeof(PagerAction), actionName);
                var state = (PagerState)State.Clone();

                switch (pagerAction)
                {
                    case PagerAction.First:
                        state.PageIndex = 0;
                        break;
                    case PagerAction.Last:
                        state.PageIndex = PageCount >= 1 ? PageCount - 1 : 1;
                        break;
                    case PagerAction.Next:
                        state.PageIndex = State.PageIndex < (PageCount - 1) ? State.PageIndex + 1 : (PageCount - 1);
                        break;
                    case PagerAction.Previous:
                        state.PageIndex = State.PageIndex > 0 ? State.PageIndex - 1 : 0;
                        break;
                    default:
                        throw new InvalidOperationException(string.Format("Pager action {0} is not supported", pagerAction));
                }

                return state;
            }
            else
                return base.OnGetState(actionName);
        }
    }
}